#!/usr/bin/env python
#!coding:utf-8
import os
import subprocess
import sys,time
import socket,errno
import signal


def _subprocess_setup():
    # Python installs a SIGPIPE handler by default. This is usually not what
    # non-Python subprocesses expect.
    signal.signal(signal.SIGPIPE, signal.SIG_DFL)

def _execute(cmd):
    stdin = subprocess.PIPE
    stdout = subprocess.PIPE
    stderr = subprocess.PIPE
    if DEBUG: 
        print cmd
        stdin = sys.stdin
        stdout = sys.stdout
        stderr = sys.stderr
    obj = subprocess.Popen(cmd,
                                   stdin=stdin,
                                   stdout=stdout,
                                   stderr=stderr,
                                   close_fds=True,
                                   preexec_fn=_subprocess_setup,
                                   shell = True
                            )
    result = obj.communicate()
    if obj.stdin :obj.stdin.close()
    _returncode = obj.returncode

    if _returncode and _returncode != 0:
        # (stdout, stderr) = result
        if DEBUG: error('return code is %s' % _returncode)
        raise Exception('return code is not 0')

# def _execute(cmd):
#     # _,out,error = os.popen3(cmd)
#     # return not error.read()
#     os.system("%s > install.log 2>&1" cmd)

_ = _execute

def ok(msg):
    print '\033[1;32m%s\033[1;m' % msg

def error(msg):
    print '\033[1;31m%s\033[1;m' % msg

def actionlog(msg):
    return '\033[1;36m%s\033[1;m' % msg

# console_args = {}


def testsocket(port,address='0.0.0.0'):
    s = socket.socket()
    try:
        s.bind((address,int(port)))
        return True
    except socket.error as e:
        if e.errno == errno.EADDRINUSE:
            error('%s:%s is in used' % (address,port))
        elif e.errno == errno.EADDRNOTAVAIL:
            error('%s is not validate' % address)
        else:
            error(e.message)
    finally:
        s.close()

def console_inputs():
    httpport = raw_input("http-port[80]:")
    while 1:
        if httpport == '': httpport = "80"
        if not httpport.isdigit() or int(httpport)<=0 or int(httpport)>=65536:
            error( "http port must be integer")
            httpport = raw_input("http-port[80]:")
        elif not testsocket(httpport):
            httpport = raw_input("http-port[80]:")
        else:
            break

    httpip = raw_input("http-ip[0.0.0.0]:")
    while 1:
        if httpip == '': httpip = '0.0.0.0'
        if not testsocket(httpport,httpip):
            httpip = raw_input("http-ip[0.0.0.0]:")
        else:
            break

    mysqlpass = raw_input("mysql password:")
    while 1:
        if not mysqlpass:
            error( "mysql password can not be null")
            mysqlpass = raw_input("mysql password:")
        else:
            break

    adminuser = raw_input("admin username[admin]:")
    while 1:
        if adminuser == '':
            adminuser = 'admin'
        if not adminuser:
            error( 'admin username can not be null')
            adminuser = raw_input("admin username[admin]:")
        else:
            break

    adminpass = raw_input("admin password:")
    while 1:
        if not adminpass:
            error( 'admin password can not be null')
            adminpass = raw_input("admin password:")
        else:
            break

    cvirtpath = raw_input("install path[/opt/tcloud/]:")
    while 1:
        if cvirtpath == '':
            cvirtpath = '/opt/tcloud/'
        if not cvirtpath.startswith('/'):
            error('install dir must be absulote path')
            cvirtpath = raw_input("install path[/opt/tcloud/]:")
        else:
            break
    return locals()

def cnode_inputs():
    #TODO TEST MYSQL CONNECTION
    consoleip = raw_input("manager node address:")
    while 1:
        if not consoleip:
            error('manager ip can not be null')
            consoleip = raw_input("manager node address:")
        else:
            break

    mysqlpass = raw_input("mysql password:")
    while 1:
        if not mysqlpass:
            error("mysql password can not be null")
            mysqlpass = raw_input("mysql password:")
        else:
            break

    localip = socket.gethostbyname(socket.gethostname())
    cnodeip = raw_input("cnode ip adress[%s]:" % localip)
    while 1:
        if cnodeip == '':
            cnodeip = localip
        if not testsocket(65534,cnodeip):
            cnodeip = raw_input("cnode ip adress[%s]:" % localip)
        else:
            break

    sshpass = raw_input("ssh password:")
    while 1:
        if not sshpass:
            error("ssh password can not be null")
            sshpass = raw_input("ssh password:")
        else:
            break

    sshport = raw_input("ssh port[22]:")
    while 1:
        if sshport == '':
            sshport = '22'
        if not sshport.isdigit():
            error('ssh port must be integer')
            sshport = raw_input("ssh port[22]:")
        else:
            break

    bondeth = raw_input("eth used for bond:")
    while 1:
        try:
            _('ip link show dev %s' % bondeth)
            break
        except:
            error('device %s not exists' % bondeth)
            bondeth = raw_input("eth used for bond:")

    cvirtpath = raw_input("install path[/opt/tcloud/]:")
    while 1:
        if cvirtpath == '':
            cvirtpath = '/opt/tcloud/'
        if not cvirtpath.startswith('/'):
            error('install dir must be absulote path')
            cvirtpath = raw_input("install path[/opt/tcloud/]:")
        else:
            break


    return locals()

def tty_handler(msg):
    def _set_msg(func):
        def _handler(*args,**kwargs):
            sys.stdout.write("%16s" % actionlog(msg))
            sys.stdout.flush()
            try:
                func(*args,**kwargs)
                ok('[done]')
            except Exception as e:
                if DEBUG: error(str(e))
                error('[failed]')
                sys.exit(1)

        return _handler
    return _set_msg


consolepackages = ['python-eventlet','python-paramiko','python-MySQLdb',
                   'python-lxml','python-imaging','python-redis',
                   'redis-server','uwsgi-core','uwsgi-plugin-python',
                    'python-libvirt','python-rrdtool','python-excelerator','hal',
                    'python-pyasn1','open-iscsi','bc']

mysqlpackages = ['mysql-server']

cnodepackages = ['python-eventlet','python-paramiko','python-MySQLdb',
                   'python-lxml','python-imaging','python-redis','bc',
                   'qemu-kvm','cifs-utils','ifenslave','vlan','libvirt-bin','nfs-common','cpu-checker','open-iscsi',
                   'python-libvirt','pm-utils','python-rrdtool','collectd','rrdtool','dnsmasq-utils','libguestfs-tools',
                     'qemu',
                     'qemu-common',
                     'qemu-utils',
                     'libusb-1.0-0',
                     'libusb-1.0-0-dev',
                     'libusbredir',
                     'libusbredir-dev',
                     'usbredir-server',
                     'libspice-protocol-dev',
                     'libspice-server-dev']




@tty_handler('install depends...')
def install_depends(packages,params):

    # cmd = 'apt-get install -y --force-yes %s ' % ' '.join(packages)
    # _(cmd)
    _('tar zxf dist-packages.tar.gz -C /usr/local/lib/python2.7/dist-packages')
    _('echo "%s" > /usr/local/lib/python2.7/dist-packages/cvirt.pth' % params['cvirtpath'])

@tty_handler('Creating database...')
def create_database(params):
    cvirtpath = params['cvirtpath']
    password = params['mysqlpass']
    _('sudo mysql -uroot -p%s -e "CREATE DATABASE cvirt DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;"' % password)
    _('sudo python %s/cvirt/manage.pyc syncdb --noinput' % cvirtpath)

@tty_handler('Setting Database Security...')
def install_mysql(params):
    password = params['mysqlpass']
    _('''cat <<MYSQL_PRESEED | sudo debconf-set-selections
mysql-server-5.5 mysql-server/root_password password %s
mysql-server-5.5 mysql-server/root_password_again password %s
mysql-server-5.5 mysql-server/start_on_boot boolean true
MYSQL_PRESEED''' % (password, password))
    _('apt-get install -y %s ' % ' '.join(mysqlpackages))

    _("sudo mysql -uroot -p"+password+" -e \"GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' identified by '"+password+"';\"")
    _("sudo mysql -uroot -p"+password+" -e \"FLUSH PRIVILEGES;\"")
    _('sed -i s/127.0.0.1/0.0.0.0/g /etc/mysql/my.cnf')

    _(r'sed -i s/\\[mysqld\\]/\\[mysqld\\]\\ntransaction-isolation=READ-COMMITTED\\ninnodb_lock_wait_timeout=30/g /etc/mysql/my.cnf')

    _("sudo service mysql restart")

@tty_handler('Editing Tcloud Configuration...')
def edit_cnode_config(params):
    _('sed -i s/mysql_password.*/mysql_password=%s/g %s/cvirt/conf/cvirt.conf' % (params['mysqlpass'],params['cvirtpath']))
    _('sed -i s/mysql_server.*/mysql_server=%s/g %s/cvirt/conf/cvirt.conf' % (params['consoleip'],params['cvirtpath']))
    _('sed -i s/broker_ip.*/broker_ip=%s/g %s/cvirt/conf/cvirt.conf' % (params['consoleip'],params['cvirtpath']))

    _('sed -i "s|CELERYD_CHDIR=\"/opt/tcloud/cvirt\"|CELERYD_CHDIR=\"%s\"|" %s/conf.d/cnode.run' % (params['cvirtpath'],params['cvirtpath']))

    #modify libvirt conf
    _('cp /etc/libvirt/libvirtd.conf /etc/libvirt/libvirtd.conf.origin')

    _(r'''echo "listen_tls = 0

listen_tcp = 1

tcp_port = \"16509\"

listen_addr = \"0.0.0.0\"

unix_sock_group = \"libvirtd\"

unix_sock_rw_perms = \"0770\"

auth_unix_ro = \"none\"

auth_unix_rw = \"none\"

auth_tcp = \"none\"" > /etc/libvirt/libvirtd.conf''')

    _('cp /etc/libvirt/qemu.conf /etc/libvirt/qemu.conf.origin')

    _(r'''echo "user = \"root\"

group = \"root\"

dynamic_ownership = 0" > /etc/libvirt/qemu.conf''')


    _(r'sed -i s/"libvirtd_opts=\"-d\""/"libvirtd_opts=\"-d -l\""/g /etc/default/libvirt-bin')

    #disable appmaor
    _('sudo ln -s /etc/apparmor.d/usr.sbin.libvirtd /etc/apparmor.d/disable/')

    _('service libvirt-bin restart')

    _('cp /etc/collectd/collectd.conf /etc/collectd/collectd.conf.origin')
    _(r'''echo "FQDNLookup true
LoadPlugin syslog
<Plugin syslog>
    LogLevel info
</Plugin>
LoadPlugin battery
LoadPlugin cpu
LoadPlugin df
LoadPlugin disk
LoadPlugin entropy
LoadPlugin interface
LoadPlugin irq
LoadPlugin libvirt
LoadPlugin load
LoadPlugin memory
LoadPlugin network
LoadPlugin processes
LoadPlugin rrdtool
LoadPlugin swap
LoadPlugin users
<Plugin df>
    FSType rootfs
    FSType sysfs
    FSType proc
    FSType devtmpfs
    FSType devpts
    FSType tmpfs
    FSType fusectl
    FSType cgroup
    IgnoreSelected true
</Plugin>
<Plugin libvirt>
        Connection \"qemu:///system\"
        RefreshInterval 60
        IgnoreSelected true
</Plugin>
<Plugin network>
    Server \"%s\" \"25826\"
    MaxPacketSize 1024
</Plugin>
<Plugin rrdtool>
    DataDir \"/var/lib/collectd/rrd\"
</Plugin>
Include \"/etc/collectd/filters.conf\"
Include \"/etc/collectd/thresholds.conf\"
" > /etc/collectd/collectd.conf''' % params['consoleip'])    

    _('/etc/init.d/collectd restart')
    
    _('sed -i s/"cnode.host.*"/"cnode.host=%s"/g %s/cvirt/conf/cvirt.conf' % (params['cnodeip'],params['cvirtpath']))
    _('sed -i s/"cnode.password.*"/"cnode.password=%s"/g %s/cvirt/conf/cvirt.conf' % (params['sshpass'],params['cvirtpath']))
    _('sed -i s/"cnode.ssh_port.*"/"cnode.ssh_port=%s"/g %s/cvirt/conf/cvirt.conf' % (params['sshport'],params['cvirtpath']))

    _('sudo ifup bond0')


@tty_handler('Editing Console Configuration...')
def edit_console_config(params):
    _('sed -i s/mysql_password.*/mysql_password=%s/g %s/cvirt/conf/cvirt.conf' % (params['mysqlpass'],params['cvirtpath']))
    _('sed -i s/"bind 127.0.0.1"/"bind 0.0.0.0"/g /etc/redis/redis.conf')
    _('sed -i s/HOST=0.0.0.0/HOST=%s/g %s/conf.d/manager.run' % (params['httpip'],params['cvirtpath']))
    _('sed -i s/PORT=8080/PORT=%s/g %s/conf.d/manager.run' % (params['httpport'],params['cvirtpath']))
    _('sed -i "s|SITE_DIR=/opt/tcloud/|SITE_DIR=%s|" %s/conf.d/manager.run' % (params['cvirtpath'],params['cvirtpath']))
    _('sed -i "s|CELERYBEAT_CHDIR=\"/opt/tcloud/cvirt\"/|CELERYBEAT_CHDIR=\"%s\"|" %s/conf.d/schedule.run' % (params['cvirtpath'],params['cvirtpath']))
    
    _('/etc/init.d/redis-server restart')

    _('cp /etc/collectd/collectd.conf /etc/collectd/collectd.conf.origin')
    _(r'''echo "FQDNLookup true
LoadPlugin syslog
<Plugin syslog>
    LogLevel info
</Plugin>
LoadPlugin battery
LoadPlugin cpu
LoadPlugin df
LoadPlugin disk
LoadPlugin entropy
LoadPlugin interface
LoadPlugin irq
LoadPlugin libvirt
LoadPlugin load
LoadPlugin memory
LoadPlugin network
LoadPlugin processes
LoadPlugin rrdtool
LoadPlugin swap
LoadPlugin unixsock
LoadPlugin users
<Plugin df>
    FSType rootfs
    FSType sysfs
    FSType proc
    FSType devtmpfs
    FSType devpts
    FSType tmpfs
    FSType fusectl
    FSType cgroup
    IgnoreSelected true
</Plugin>

<Plugin network>
    Listen \"0.0.0.0\" \"25826\"
</Plugin>
<Plugin rrdtool>
    DataDir \"/var/lib/collectd/rrd\"
</Plugin>
<Plugin unixsock>
    SocketFile \"/var/run/collectd-unixsock\"
    SocketGroup \"collectd\"
    SocketPerms \"0770\"
    DeleteSocket false
</Plugin>
Include \"/etc/collectd/filters.conf\"
Include \"/etc/collectd/thresholds.conf\"
" > /etc/collectd/collectd.conf''') 
    _('/etc/init.d/collectd restart')

@tty_handler('Starting Tcloud service...')
def start_service(shell,action='start'):
    _('/etc/init.d/%s %s' % (shell,action))

@tty_handler('Setup up bond for data network...')
def setup_bond(params):
    bondeth = params['bondeth']
    _('''echo "auto %(eth)s
iface %(eth)s inet manual
bond-master bond0

auto bond0
iface bond0 inet manual
bond-mode 802.3ad
bond-miimon 100
bond-lacp-rate 1
bond-slaves none" >> /etc/network/interfaces''' % dict(eth=bondeth))


@tty_handler('Deploying Tcloud Service...')
def deloy_cnode(params):
    cvirtpath = params['cvirtpath']
    _('tar zxf cvirt.tar.gz -C %s' % cvirtpath)
    _('cp %(path)s/cvirt/deploy/rc-scripts/conf.d/cnode.run %(path)s/conf.d/' % dict(path=cvirtpath))
    _('chmod +x %s/cvirt/deploy/rc-scripts/init.d/celeryd'% cvirtpath)
    _('ln -s %s/cvirt/deploy/rc-scripts/init.d/celeryd /etc/init.d/' % cvirtpath)
    _('update-rc.d celeryd defaults')
    

@tty_handler('Deploying Console Service...')
def deloy_console(params):
    cvirtpath = params['cvirtpath']
    _('tar zxf cvirt.tar.gz -C %s' % cvirtpath)
    _('cp wsgi.py %s/cvirt' % cvirtpath)
    for f in ['manager','schedule']:
        _('cp %(path)s/cvirt/deploy/rc-scripts/conf.d/%(f)s.run %(path)s/conf.d/' % dict(f=f,path=cvirtpath))

    for f in ['celerybeat','console']:
        _('chmod +x %s/cvirt/deploy/rc-scripts/init.d/%s'% (cvirtpath,f))
        _('ln -s %s/cvirt/deploy/rc-scripts/init.d/%s /etc/init.d/' % (cvirtpath,f))
        _('update-rc.d %s defaults' % f)

@tty_handler('make sure cvirt path...')
def make_sure_dis(params):
    _('mkdir -p %s/conf.d' % params['cvirtpath'])

@tty_handler('Create super user...')
def create_super_user(params):
    _(r'echo "from django.contrib.auth.models import User; User.objects.create_superuser(\"%s\", \"admin@admin.com\", \"%s\")" | python %s/cvirt/manage.pyc shell' % (
            params['adminuser'],params['adminpass'],params['cvirtpath']
        ))

def clean():
    _('apt-get clean')


def add_source_list():
    _('cp /etc/apt/sources.list /etc/apt/sources.list.bak')
    with file('/etc/apt/sources.list','w') as f:
        f.write('''deb http://mirrors.sohu.com/ubuntu/ precise main restricted universe multiverse
deb http://mirrors.sohu.com/ubuntu/ precise-security main restricted universe multiverse
deb http://mirrors.sohu.com/ubuntu/ precise-updates main restricted universe multiverse
deb http://mirrors.sohu.com/ubuntu/ precise-proposed main restricted universe multiverse
deb http://mirrors.sohu.com/ubuntu/ precise-backports main restricted universe multiverse

deb http://ppa.launchpad.net/raravena80/collectd5/ubuntu precise main
deb http://ppa.launchpad.net/bderzhavets/lib-usbredir81/ubuntu precise main''')
    _('apt-get update')

def install_console():
    params = console_inputs()
    print actionlog('Installing...')
    # add_source_list()
    # _('cp all/*.deb /var/cache/apt/archives')
    # _('cp console/*.deb /var/cache/apt/archives')
    install_depends(consolepackages,params)
    make_sure_dis(params)
    deloy_console(params)
    install_mysql(params)

    edit_console_config(params)# 在生成数据库和管理员账号前修改settings.py
    
    create_database(params)
    create_super_user(params)
    
    start_service('console','start')
    start_service('celerybeat','start')
    clean()
    ok('*******install console successful*****')


def install_cnode():
    try:
        if not DEBUG:
            _('kvm-ok')
    except:
        error("your system do not support VT-x/AMD-V,please reconfirm!")
        sys.exit(1)

    params = cnode_inputs()
    print actionlog('Installing...')
    # add_source_list()
    # _('cp all/*.deb /var/cache/apt/archives')
    # _('cp cnode/*.deb /var/cache/apt/archives')
    install_depends(cnodepackages,params)

    #apply netfilter patch
    # _('sudo dpkg -i libvirt-bin_0.9.8-2ubuntu17.10_amd64.deb')

    make_sure_dis(params)
    deloy_cnode(params)
    edit_cnode_config(params)
    setup_bond(params)
    start_service('celeryd','start')
    clean()
    ok('*******install cnode successful*****')


DEBUG = True

def set_debug(debug):
    global DEBUG
    DEBUG = debug

if __name__ == '__main__':
    from optparse import OptionParser
    parser = OptionParser()
    parser.add_option("--debug", dest="debug",action="store_true",
                      help="install use debug mode")

    (options, args) = parser.parse_args()
    set_debug(options.debug)

    if args:
        if args[0] == 'cnode':
            install_cnode()
        elif args[0] == 'console':
            install_console()
        else:
            ok('usage: [console|cnode]')
    else:
        # ok('usage: [console|cnode]')
        parser.print_help()


